package top.wilsonlv.jaguar.cloud.upms.service;

import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;
import top.wilsonlv.jaguar.basecrud.BaseModel;
import top.wilsonlv.jaguar.basecrud.BaseService;
import top.wilsonlv.jaguar.cloud.upms.entity.Menu;
import top.wilsonlv.jaguar.cloud.upms.entity.Role;
import top.wilsonlv.jaguar.cloud.upms.entity.RoleMenu;
import top.wilsonlv.jaguar.cloud.upms.mapper.RoleMenuMapper;
import top.wilsonlv.jaguar.cloud.upms.sdk.vo.RoleVO;
import top.wilsonlv.jaguar.commons.mybatisplus.extension.JaguarLambdaQueryWrapper;
import top.wilsonlv.jaguar.commons.web.exception.impl.CheckedException;
import top.wilsonlv.jaguar.security.model.SecurityAuthority;
import top.wilsonlv.jaguar.security.model.SecurityUser;
import top.wilsonlv.jaguar.security.util.SecurityUtil;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;

/**
 * <p>
 * 系统角色菜单表 服务实现类
 * </p>
 *
 * @author lvws
 * @since 2019-11-08
 */
@Service
@RequiredArgsConstructor
public class RoleMenuService extends BaseService<RoleMenu, RoleMenuMapper> {

    private final RoleService roleService;

    private final MenuService menuService;

    /*----------  通用接口  ----------*/

    public Set<String> listPermissionsByRoleIds(Set<Long> roleIds) {
        return this.mapper.listPermissionsByRoleIds(roleIds);
    }

    /*---------- 权限管理 ----------*/

    @Transactional
    public void relateMenus(Long roleId, Set<Long> menuIds, Set<Long> indexMenuIds) {
        if (CollectionUtils.isEmpty(menuIds)) {
            this.delete(Wrappers.lambdaQuery(RoleMenu.class)
                    .eq(RoleMenu::getRoleId, roleId)
                    .eq(RoleMenu::getBuiltIn, false));
            return;
        }

        if (!CollectionUtils.isEmpty(indexMenuIds)) {
            if (!menuIds.containsAll(indexMenuIds)) {
                throw new CheckedException("首页菜单必须为已授权菜单");
            }

            List<Menu> indexMenus = menuService.list(Wrappers.lambdaQuery(Menu.class)
                    .in(BaseModel::getId, indexMenuIds));
            for (int i = 0; i < indexMenus.size(); i++) {
                Menu one = indexMenus.get(i);
                for (int j = i + 1; j < indexMenus.size(); j++) {
                    Menu another = indexMenus.get(j);
                    if (one.getResourceServerId().equals(another.getResourceServerId())
                            && menuService.hasCommonClientType(one.getClientTypes(), another.getClientTypes())) {
                        throw new CheckedException("同一系统下的一个客户端只能设置唯一首页");
                    }
                }
            }
        }

        for (Long menuId : menuIds) {
            RoleMenu unique = this.unique(Wrappers.lambdaQuery(RoleMenu.class)
                    .eq(RoleMenu::getRoleId, roleId)
                    .eq(RoleMenu::getMenuId, menuId));
            if (unique != null) {
                unique.setIndex(indexMenuIds.contains(menuId));
                this.updateById(unique);
                continue;
            }

            Menu menu = menuService.getById(menuId);

            RoleMenu roleMenu = new RoleMenu();
            roleMenu.setRoleId(roleId);
            roleMenu.setMenuId(menu.getId());
            roleMenu.setBuiltIn(false);
            roleMenu.setIndex(indexMenuIds.contains(menuId));
            this.insert(roleMenu);
        }

        this.delete(Wrappers.lambdaQuery(RoleMenu.class)
                .eq(RoleMenu::getRoleId, roleId)
                .eq(RoleMenu::getBuiltIn, false)
                .notIn(RoleMenu::getMenuId, menuIds));
    }

    @Transactional
    public List<RoleVO> listGrantedRoles(String fuzzyRoleName) {
        List<Role> roles = roleService.list(JaguarLambdaQueryWrapper.<Role>newInstance()
                .like(Role::getRoleName, fuzzyRoleName)
                .eq(Role::getRoleEnable, true));

        SecurityUser currentUser = SecurityUtil.getCurrentUser();
        if (currentUser.getBuiltIn()) {
            return roles.stream().map(role -> role.toVo(RoleVO.class)).collect(Collectors.toList());
        }

        Set<String> total = currentUser.getAuthorities().stream().map(SecurityAuthority::getAuthority).collect(Collectors.toSet());
        List<RoleVO> roleVOS = new ArrayList<>();
        for (Role role : roles) {
            Set<String> permissions = this.listPermissionsByRoleIds(Collections.singleton(role.getId()));
            if (total.containsAll(permissions)) {
                roleVOS.add(role.toVo(RoleVO.class));
            }
        }
        return roleVOS;
    }
}
